/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 string_table.cpp

	$Header: /resource_editor/string_table.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "string_table.h"

#include <algorithm>

#include "sized_int_types.h"
#include "streambuf_operators.h"
#include "string_insensitive_compare.h"

char const t_resource_traits<t_string_table>::prefix[] = "strings";

char const t_resource_traits<t_string_table>::extension[] = "txt";

namespace {
	// --------------------------------------------
	// comparison function object
	// --------------------------------------------
	class t_compare_keys
	{
	public:
		t_compare_keys( t_table const& table ) : m_table( table )
		{
		}

		bool operator()( t_table::size_type left, t_table::size_type right ) const
		{
			return string_insensitive_compare( m_table[ left ][ 0 ], m_table[ right ][ 0 ] ) < 0;
		}

		bool operator()( t_table::size_type left, std::string const & right ) const
		{
			return string_insensitive_compare( m_table[ left ][ 0 ], right ) < 0;
		}

		bool operator()( std::string const & left, t_table::size_type right ) const
		{
			return string_insensitive_compare( left, m_table[ right ][ 0 ] ) < 0;
		}

	private:
		t_table const& m_table;
	};

	// --------------------------------------------
	// --------------------------------------------
	inline bool is_table_entry( t_string_vector const & row )
	{
		if ( row.size() < 2 )
			return false;

		std::string const & key = row.front();
		return !key.empty() && ( key.size() < 2 || key[ 0 ] != '/' || key[ 1 ] != '/' );
	}
};

// --------------------------------------------
// --------------------------------------------
t_string_table::t_index_vector t_string_table::create_index_vector(
	t_table const &	table )
{
	t_index_vector result;

	t_table::const_iterator table_begin = table.begin();
	t_table::const_iterator table_end = table.end();
	t_table::const_iterator table_iter = table_begin;
	for ( ; table_iter != table_end; ++table_iter )
	{
		if ( is_table_entry( *table_iter ) )
			result.push_back( table_iter - table_begin );
	}
	std::sort( result.begin(), result.end(), t_compare_keys( table ) );

	return result;
}

// --------------------------------------------
// --------------------------------------------
t_string_table::t_string_table( t_string_table const & other )
	:	m_table( other.m_table ),
		m_index_vector( other.m_index_vector )
{
}

// --------------------------------------------
// constructs a copy of the string table but
// appends a specified suffix to each of the keys
// --------------------------------------------
t_string_table::t_string_table(
	t_string_table const &	other,
	std::string const &		key_suffix )
	:	m_table( other.m_table )
{
	t_table::iterator table_end = m_table.end();
	t_table::iterator table_iter = m_table.begin();
	for ( ; table_iter != table_end; ++table_iter )
	{
		if ( is_table_entry( *table_iter ) )
		{
			std::string & key = table_iter->front();
			key += '.';
			key += key_suffix;
		}
	}
	create_index_vector( m_table ).swap( m_index_vector );
}

// --------------------------------------------
// find the correct string, given a key value.
// --------------------------------------------
t_string_table::t_index_vector::const_iterator
t_string_table::do_find( std::string const & key ) const
{
	t_index_vector::const_iterator result;

	result =	std::lower_bound(
					m_index_vector.begin(),
					m_index_vector.end(),
					key,
					t_compare_keys( m_table ) );

	if (	result == m_index_vector.end()
		||	string_insensitive_compare( m_table[ *result ][ 0 ], key ) != 0 )
		return m_index_vector.end();

	return result;
}

// --------------------------------------------
// import strings from another table and sort them.
// --------------------------------------------
void t_string_table::import( t_table const& table )
{
	t_table new_table = table;
	create_index_vector( new_table ).swap( m_index_vector );
	new_table.swap( m_table );
}

// --------------------------------------------
// find location to insert value.  Replaces old value if found.
// --------------------------------------------
void t_string_table::insert( std::string const & key, std::string const & value )
{
	t_index_vector::iterator index_iter;
	index_iter =	std::lower_bound(
						m_index_vector.begin(),
						m_index_vector.end(),
						key,
						t_compare_keys( m_table ) );

	if (	index_iter == m_index_vector.end()
		||	string_insensitive_compare( m_table[ *index_iter ][ 0 ], key ) != 0 )
	{
		m_index_vector.insert( index_iter, m_table.size() );
		m_table.push_back( t_table_row() );
		m_table.back().resize( 2 );
		m_table.back()[ 0 ] = key;
		m_table.back()[ 1 ] = value;
	}
	else
		m_table[ *index_iter ][ 1 ] = value;
}

// --------------------------------------------
// read the string table from a stream buffer
// --------------------------------------------
bool t_string_table::read( std::streambuf& buffer )
{
	t_table new_table;
	t_index_vector new_index_vector;

	if ( !new_table.read( buffer ) )
		return false;

	try
	{
		int count = get< t_uint32 >( buffer );

		new_index_vector.resize( count );
		int index;
		for ( index = 0; index < count; ++index )
			new_index_vector[ index ] = get< t_uint32 >( buffer );
	}
	catch ( t_streambuf_read_failure const & )
	{
		return false;
	}

	new_table.swap( m_table );
	new_index_vector.swap( m_index_vector );

	return true;
}

// --------------------------------------------
// write the string table and index to the stream buffer
// --------------------------------------------
bool t_string_table::write( std::streambuf& buffer ) const
{
	if ( !m_table.write( buffer ) )
		return false;

	try
	{
		put< t_uint32 >( buffer, m_index_vector.size() );
		t_index_vector::const_iterator index_iter;
		for ( index_iter = m_index_vector.begin(); index_iter != m_index_vector.end(); ++index_iter )
			put< t_uint32 >( buffer, *index_iter );
	}
	catch ( t_streambuf_write_failure const & )
	{
		return false;
	}

	return true;
}

// --------------------------------------------
// --------------------------------------------
t_string_table & t_string_table::operator=( t_string_table const & other )
{
	t_string_table copy( other );
	m_table.swap( copy.m_table );
	m_index_vector.swap( copy.m_index_vector );

	return *this;
}
